﻿/*****************************************************************************\
 *             University of Illinois/NCSA Open Source License
 *
 * 
 *      Copyright © 2012,2013, The Board of Trustees of the University of Illinois. All rights reserved.
 *
 *      Developed by: Rakesh Bobba, Derek Dagit, Erich Heine, Denis Kholine,
 *                    Klara Nahrstedt, David Rodgers,   William H. Sanders, Tim Yardley
 *
 *      Information Trust Institute/Coordinated Science Laboratory
 * 
 *      University of Illinois
 *      
 *      The Board of Trustees of the University of Illinois
 *      109 Coble Hall
 *      801 South Wright Street
 *      Champaign, IL 61820-6242
 *
 *      http://www.iti.illinois.edu / http://csl.illinois.edu
 * 
 *      Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 
 *      associated documentation files (the “Software”), to deal with the Software without restriction, 
 *      including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *      and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
 *      subject to the following conditions:
 *
 *      Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *      following disclaimers.
 *      
 *      Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
 *      the following disclaimers in the documentation and/or other materials provided with the distribution.
 *
 *      Neither the names of Information Trust institute, Coordinated Science Laboratory, University of Illinois,
 *      The Board of Trustees of the University of Illinois nor the names of its contributors may be used to 
 *      endorse or promote products derived from this Software without specific prior written permission.
 *      
 *      THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 *      LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN
 *      NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *      WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 *      SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 *      
 \*****************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using Disruptor;
using SIEGate.Core.Adapters;


namespace SIEGate.Core.MessageBusses
{
    internal class PublishedEventArgs : EventArgs
    {
        public ISet<Guid> NewPubs;

        public PublishedEventArgs(ISet<Guid> newPubs)
        {
            NewPubs = newPubs;
        }
    }
    public class ChannelMessageBus : IMessageBus
    {
        private List<ChannelMessageBusProxy> m_proxies;
        private int default_buffsize;
        private HashSet<Guid> m_pendingSubs = new HashSet<Guid>();

        private class PollingWaitStrategy : IWaitStrategy
        {
            public long WaitFor(long sequence, Sequence cursor, Sequence[] dependents, ISequenceBarrier barrier)
            {
                long minimumSequence;
                if (dependents.Length == 0)
                {
                    if ((minimumSequence = cursor.Value) < sequence)
                        barrier.CheckAlert();
                }
                else
                {
                    if ((minimumSequence = GetMinimumSequence(dependents)) < sequence)
                        barrier.CheckAlert();
                }
                return minimumSequence;
            }

            public long WaitFor(long sequence, Sequence cursor, Sequence[] dependents, ISequenceBarrier barrier, TimeSpan timeout)
            {
                long minimumSequence;
                if (dependents.Length == 0)
                {
                    if ((minimumSequence = cursor.Value) < sequence)
                        barrier.CheckAlert();
                }
                else
                {
                    if ((minimumSequence = GetMinimumSequence(dependents)) < sequence)
                        barrier.CheckAlert();
                }
                return minimumSequence;
            }

            public void SignalAllWhenBlocking()
            {
            }

            private static long GetMinimumSequence(Sequence[] sequences)
            {
                if (sequences.Length == 0)
                    return long.MaxValue;
                long num1 = long.MaxValue;
                for (int index = 0; index < sequences.Length; ++index)
                {
                    long num2 = sequences[index].Value;
                    num1 = num1 < num2 ? num1 : num2;
                }
                return num1;
            }
        }

        public ChannelMessageBus(int buffsize = 2048)
        {
            default_buffsize = buffsize;
            m_proxies = new List<ChannelMessageBusProxy>();
        }

        internal ChannelMessageBusProducer GetProducer(ISet<Guid> streamIDs, QOSParams qos)
        {
            // TODO: DONT HARDCODE SIZE FOR RINGBUFFER
            RingBuffer<Routable> ringBuff = new RingBuffer<Routable>(() => new Routable(),
                new SingleThreadedClaimStrategy(default_buffsize),
                new PollingWaitStrategy());
            ChannelMessageBusProducer res =  new ChannelMessageBusProducer(streamIDs, qos, ringBuff);
            res.NewPubs += res_NewPubs;
            //res.Publish(streamIDs);
            return res;
        }

        void res_NewPubs(object sender, PublishedEventArgs e)
        {
            if (e.NewPubs.Intersect(m_pendingSubs).Any())
            {
                LogDispatcher.I("MBus", "Subscribe", "Actually doing newpub stuff for:" + e.NewPubs.Select(mguid=> mguid.ToString()).Aggregate((a,b) => a + "," + b));
                foreach (ChannelMessageBusProxy mbp in m_proxies)
                {
                    LogDispatcher.D("Foo", "D", "Calling subscribe");

                    mbp.Subscribe(new HashSet<Guid>(e.NewPubs.Intersect(mbp.Subscribed)));
                }
                m_pendingSubs.ExceptWith(e.NewPubs);
            }
        }

        internal List<ChannelMessageBusConsumer> GetConsumers(ISet<Guid> streamIDs)
        {
            List<ChannelMessageBusConsumer> res = m_proxies.Where(proxy => proxy.Producer != null)
                .Select(proxy => proxy.Producer).Where(prod => prod.StreamIDs.Intersect(streamIDs)
                    .Any()).Select(p => new ChannelMessageBusConsumer(p, streamIDs)).ToList();
            HashSet<Guid> check = new HashSet<Guid>();
            foreach (ISet<Guid> avail in res.Select(consumer => consumer.AvailableStreamIDs))
            {
                check.UnionWith(avail);
            }
            m_pendingSubs.UnionWith(streamIDs.Except(check));

            return res;

        }
        public IMessageBusProxy GetProxy(string name)
        {
            ChannelMessageBusProxy res = new ChannelMessageBusProxy(this);
            m_proxies.Add(res);
            return res;
        }

        public void EnablePath(Guid messageId, OutputAdapter adapter)
        {
            throw new NotImplementedException("Enable Path is not implemented");
        }

        public void DisablePath(Guid messageId, OutputAdapter adapter)
        {
            throw new NotImplementedException("Disable Path is not implemented");
        }
        public void Stats()
        {
            Console.WriteLine("Stats for channel bus:");
            Console.WriteLine("TODO: FIXME");
        }
    }
}
